// -- pixel shader resources and sampler
static const unsigned int kMaxConeLights = 4;
Texture2D shadowmap[kMaxConeLights] : register(t0);
SamplerState sample : register(s0);

// ================================================================================================
// Constant buffer for world, camera, and projection matrices
// ================================================================================================
cbuffer ConstantBuffer {
    matrix world;
    matrix view;
    matrix proj;
    matrix tinvworld;
    matrix lightviews[kMaxConeLights];
}

// ================================================================================================
// ConeLight struct definition
// ================================================================================================
struct ConeLight {
    float4 dir : TEXCOORD0;
    float4 pos : TEXCOORD0;
    float4 col : Color;
    float4 penumbra;
};

// ================================================================================================
// Constant buffer for lights
// ================================================================================================
cbuffer LightBuffer {
    float4 dirlights[4];
    float4 dirclrs[4];
    float4 ptlights[4];
    float4 ptclrs[4];
    ConeLight conelights[kMaxConeLights];
}

// ================================================================================================
// Vertex Input struct definition
// ================================================================================================
struct VertexIn {
    float4 pos : SV_Position;
    float4 col : Color;
    float3 norm : Normal;
};

// ================================================================================================
// Vertex Output struct definition
// ================================================================================================
struct VertexOut {
    float4 pos : SV_POSITION;
    float4 wpos : TEXCOORD0;
    float4 lpos[kMaxConeLights] : TEXCOORD1;  
    float4 col : Color;
    float3 norm : Normal;
};

// =================================================================================================
// Helper for function for spot light computation
// =================================================================================================
void ConeLightComputation(in float4 srfcolor, in float3 worldpos, in float3 surfnorm, 
                          in ConeLight light, inout float4 col) {
    float3 dirtosurface = worldpos - light.pos.xyz;
    float maglight = 1.0f/dot(dirtosurface,dirtosurface);
    dirtosurface = normalize(dirtosurface);
    float var = dot(dirtosurface, light.dir.xyz);

    float penumbracoef = pow((var - light.pos.w)/(light.penumbra.x - light.pos.w),1);
    
    if(var <= light.pos.w) 
        return;
    if(var > light.pos.w && var < light.penumbra.x) 
        col += saturate(dot(-dirtosurface, surfnorm) * light.dir.w 
                           * maglight * light.col * penumbracoef * srfcolor);
    if(var >= light.penumbra.x) 
       col += saturate(dot(-dirtosurface, surfnorm) * light.dir.w 
                          * maglight * light.col * srfcolor);
}

// ================================================================================================ 
// Vertex Shader
// ================================================================================================ 
VertexOut VSMain(VertexIn vert) {
    VertexOut output = (VertexOut)0;
    output.pos = mul(vert.pos, world);
    output.wpos = mul(vert.pos, world);
    output.pos = mul(output.pos, view);
    output.pos = mul(output.pos, proj);
    output.norm = mul(float4(vert.norm,0), tinvworld).xyz;
    float4 wpos = mul(vert.pos, world);
    output.lpos[0] = mul(wpos, lightviews[0]);
    output.lpos[0] = mul(output.lpos[0], proj);
    output.lpos[1] = mul(wpos, lightviews[1]);
    output.lpos[1] = mul(output.lpos[1], proj);
    output.lpos[2] = mul(wpos, lightviews[2]);
    output.lpos[2] = mul(output.lpos[2], proj);
    output.lpos[3] = mul(wpos, lightviews[3]);
    output.lpos[3] = mul(output.lpos[3], proj);
 
    output.col = vert.col;

    return output;
}

// ================================================================================================
// Pixel Shader
// ================================================================================================
float4 PSMain(VertexOut input) : SV_Target {
    // -- Some blank color
    float4 col = {0.0f, 0.0f, 0.0f, 1.0f};

    // -- normalize the input
    float3 norm = normalize(input.norm);

    for(unsigned int i = 0; i < kMaxConeLights; ++i) {
        input.col.x = 1.0f;
        input.col.y = 1.0f;
        input.col.z = 1.0f;
        float2 shadowtex = 0.5 * input.lpos[i].xy/input.lpos[i].w + float2(0.5, 0.5);
	    shadowtex.y = 1.0f - shadowtex.y;
	    float contribution = 0.0f;

        // -- transform from projection space to texture space
        float delta = 0.0001f;
        float sampled = shadowmap[i].Sample(sample, shadowtex).x;
    	float sampledel = sampled + delta;
    	float storeyoulater = input.lpos[i].z/input.lpos[i].w;
    	if(sampledel < storeyoulater)
    	    input.col *= 0.0f;
    	else 
    	    input.col *= 1.0f;

        ConeLightComputation(input.col, input.wpos, norm, conelights[i], col);

    	// -- calculate the point lighting
    	//for(int i = 0; i < 4; ++i) {
    	//    float3 dirtosurface = input.wpos - ptlights[i];
    	//	float invmagsqrd = 1.0f/dot(dirtosurface, dirtosurface);
    	//	dirtosurface = normalize(dirtosurface);
    	//    col += saturate(-dot(dirtosurface, norm) * invmagsqrd * ptlights[i].w * (input.col * ptclrs[i]));
    	//}

    	// -- calculate the dir lighting
    	//for(int i = 0; i < 4; ++i) {
    	//    col += saturate(-dot(dirlights[i], norm) * dirlights[i].w * (input.col * dirclrs[i]));  
        //{
    }

    return col;
}

// ================================================================================================
// Different vtx shader
// ================================================================================================
float4 VTX(VertexIn input) : SV_POSITION {
    float4 pos;
    pos = mul(input.pos, world);
    pos = mul(pos, view);
    pos = mul(pos, proj);

    return pos;
}

// =================================================================================================
// Different pix shader
// =================================================================================================
float PS(float4 pos : SV_POSITION) : SV_Target {
    return saturate(pos.z);
}